本节对AXI DMA的进行了基本介绍,并通过AXI DMA环路实验简单演示了AXI DMA IP核的使用
AXI DMA简介
AXI DMA:AXI Direct Memory Access 直接内存访问
- AXI DMA 为内存和AXI4—Stream外设之间提供了高带宽的直接内存访问
- 其可选的S/G功能可以将CPU从数据搬运任务中解放出来
- AXI DMA 通过AXI4-Lite接口对寄存器做一些配置和获取
- 使用AXI_DMA可以大大提高数据传输的效率和速度。它通过直接访问主存中的数据,避免了CPU的介入,从而实现了高速、并行的数据传输。而且,由于AXI_DMA是专门设计用于处理数据传输的协议,它提供了一系列优化的特性,如数据缓冲、数据对齐、FIFO和错误处理等,可以更好地满足系统的性能需求
MM2S:MemoryMap to Stream ,存储器映射(AXI4—Full)到 AXI4—Stream
S2MM:Stream to MemoryMap ,AXI4—Stream到存储器映射(AXI4—Full)
AXI DMA 编程顺序:
- Direct Register Mode (简单DMA):
- 此模式提供了在MM2S和S2MM通道上进行简单DMA传输的配置,只需较少的FPGA资源,通过访问DMACR、源地址或者目的地址和长度寄存器发起DMA传输。当传输完成后,如果使能了产生中断输出,那么DMASR寄存器相关联的通道位会有效
- DMA的MM2S(存储器映射到Stream)通道的启动顺序:
- 开启/使能MM2S通道
- 如果需要的话,可以使能中断
- 写一个有效的源地址到MM2S_SA寄存器:如果没有使能DRE的功能,在指定起始地址时,需要注意字节地址对齐,哪些地址是对齐或者不对齐的, 取决于Stream流的数据位宽
- 写传输的字节数到MM2S_LENGTH寄存器:一个长度为0的值是无效的,而一个非0值,将会决定存储器映射到Stream流的数据个数。需要注意的是,必须最后一个配置MM2S_LENGTH寄存器,而其他寄存器的配置顺序没有要求
- Direct Register Mode (简单DMA):
S/G模式:
- 它把传输的基本参数存储在内存中,这些参数就是被称为BD(Buffer Descriptor ),在工作时,通过SG接口加载和更新BD中的状态。
AXI DMA环路测试实验
实验任务:使用PL的AXI DMA IP核从DDR3中读取数据,并将数据写回到DDR3中
1.Vivado中的相关配置
ZYNQ processing system的配置:自动配置好后,还需要加上如下配置
添加AXI4-Stream Data FIFO IP核模拟一个stream流外设
添加Concat ip核合并中断端口
最终Block框图如下:
2.AXI DMA IP核的使用
3.Vitis中的代码编写
使用Xilinx提供的简单DMA中断示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/************************** Constant Definitions *****************************/
/************************** Function Prototypes ******************************/
static int check_data(int length, u8 start_value);
static void tx_intr_handler(void *callback);
static void rx_intr_handler(void *callback);
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id);
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id);
/************************** Variable Definitions *****************************/
static XAxiDma axidma; //XAxiDma实例
static XScuGic intc; //中断控制器的实例
volatile int tx_done; //发送完成标志
volatile int rx_done; //接收完成标志
volatile int error; //传输出错标志
/************************** Function Definitions *****************************/
int main(void)
{
int i;
int status;
u8 value;
u8 *tx_buffer_ptr;
u8 *rx_buffer_ptr;
XAxiDma_Config *config;
tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;
xil_printf("\r\n--- Entering main() --- \r\n");
config = XAxiDma_LookupConfig(DMA_DEV_ID);
if (!config) {
xil_printf("No config found for %d\r\n", DMA_DEV_ID);
return XST_FAILURE;
}
//初始化DMA引擎
status = XAxiDma_CfgInitialize(&axidma, config);
if (status != XST_SUCCESS) {
xil_printf("Initialization failed %d\r\n", status);
return XST_FAILURE;
}
if (XAxiDma_HasSg(&axidma)) {
xil_printf("Device configured as SG mode \r\n");
return XST_FAILURE;
}
//建立中断系统
status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
if (status != XST_SUCCESS) {
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
//初始化标志信号
tx_done = 0;
rx_done = 0;
error = 0;
value = TEST_START_VALUE;
for (i = 0; i < MAX_PKT_LEN; i++) {
tx_buffer_ptr[i] = value;
value = (value + 1) & 0xFF;
}
Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache
status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN); //刷新Data Cache
while (!tx_done && !rx_done && !error)
;
//传输出错
if (error) {
xil_printf("Failed test transmit%s done, "
"receive%s done\r\n", tx_done ? "" : " not",
rx_done ? "" : " not");
goto Done;
}
//传输完成,检查数据是否正确
status = check_data(MAX_PKT_LEN, TEST_START_VALUE);
if (status != XST_SUCCESS) {
xil_printf("Data check failed\r\n");
goto Done;
}
xil_printf("Successfully ran AXI DMA Loop\r\n");
disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
Done: xil_printf("--- Exiting main() --- \r\n");
return XST_SUCCESS;
}
//检查数据缓冲区
static int check_data(int length, u8 start_value)
{
u8 value;
u8 *rx_packet;
int i = 0;
value = start_value;
rx_packet = (u8 *) RX_BUFFER_BASE;
for (i = 0; i < length; i++) {
if (rx_packet[i] != value) {
xil_printf("Data error %d: %x/%x\r\n", i, rx_packet[i], value);
return XST_FAILURE;
}
value = (value + 1) & 0xFF;
}
return XST_SUCCESS;
}
//DMA TX中断处理函数
static void tx_intr_handler(void *callback)
{
int timeout;
u32 irq_status;
XAxiDma *axidma_inst = (XAxiDma *) callback;
//读取待处理的中断
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
//确认待处理的中断
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
//Tx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Tx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
tx_done = 1;
}
//DMA RX中断处理函数
static void rx_intr_handler(void *callback)
{
u32 irq_status;
int timeout;
XAxiDma *axidma_inst = (XAxiDma *) callback;
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
//Rx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Rx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
rx_done = 1;
}
//建立DMA中断系统
// @param int_ins_ptr是指向XScuGic实例的指针
// @param AxiDmaPtr是指向DMA引擎实例的指针
// @param tx_intr_id是TX通道中断ID
// @param rx_intr_id是RX通道中断ID
// @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id)
{
int status;
XScuGic_Config *intc_config;
//初始化中断控制器驱动
intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == intc_config) {
return XST_FAILURE;
}
status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
intc_config->CpuBaseAddress);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
//设置优先级和触发类型
XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
//为中断设置中断处理函数
status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
(Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
(Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
XScuGic_Enable(int_ins_ptr, tx_intr_id);
XScuGic_Enable(int_ins_ptr, rx_intr_id);
//启用来自硬件的中断
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
(void *) int_ins_ptr);
Xil_ExceptionEnable();
//使能DMA中断
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
return XST_SUCCESS;
}
//此函数禁用DMA引擎的中断
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id)
{
XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
}
4.实验结果
终端显示